//
//  DnSuperImageView.m
//  DnImageCropper
//
//  Created by Ngoc on 09/12/2011.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//
#define ZOOM_STEP 1.5
#define kMaxRedoOperator 10
#import "DnSuperImageView.h"
@interface NSMutableArray (Stack)
-(void) push:(id) object;
-(id) pop;
@end
@implementation NSMutableArray (Stack_Impl)

-(void) push:(id) object
{
    if ([self count] == kMaxRedoOperator-1) {
        [self removeLastObject];
    }
        [self insertObject:object atIndex:0];
}
-(id) pop
{
    id object = nil;
    if ([self count]>0) {
        object  = [self objectAtIndex:0];
        [self removeObjectAtIndex:0];
    }
    return object;
}

@end

@interface DnSuperImageView(Private)

-(void) setUpWithDefaultValue;
-(void) updateScrollView;
- (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center;
-(void) changeImageSizeAndUpdateScrollView;
- (void) sliderMoved:(float) newValue;
-(void) saveImageToRedo:(UIImage*) repairingImage;
-(void) redo;
-(void) undo;
@end


@implementation DnSuperImageView
@synthesize resizeStrategy =_resizeStrategy;
@synthesize currentSliderValue = _currentSliderValue;

- (id)initWithFrame:(CGRect)frame image:(UIImage*) image	
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setUpWithDefaultValue]; 
        _imageView = [[UIImageView alloc] initWithImage:image];
        _imageView.userInteractionEnabled = YES;
        _imageView.frame = CGRectMake(0,0, _imageView.image.size.width, _imageView.image.size.height);
        
        _scrollView = [[NYOBetterZoomUIScrollView alloc] initWithChildView:_imageView];
        _scrollView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
        _scrollView.delegate = self;
        _scrollView.childView = _imageView;
        _scrollView.contentSize = _imageView.frame.size;
        _scrollView.maximumZoomScale   = _maximumZoomScale;
        _scrollView.minimumZoomScale = 1.0;
        _scrollView.showsHorizontalScrollIndicator = NO;
        _scrollView.showsVerticalScrollIndicator = NO;
        _scrollView.backgroundColor = [UIColor blueColor];
        self.backgroundColor = [UIColor orangeColor];
        [self addSubview:_scrollView];
        [self updateScrollView];
        
        // add gesture recognizers to the image view
        UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
        UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
        UITapGestureRecognizer *twoFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerTap:)];
        
        [doubleTap setNumberOfTapsRequired:2];
        [twoFingerTap setNumberOfTouchesRequired:2];
        
        [_imageView addGestureRecognizer:singleTap];
        [_imageView addGestureRecognizer:doubleTap];
        [_imageView addGestureRecognizer:twoFingerTap];
        
        [singleTap release];
        [doubleTap release];
        [twoFingerTap release];
        _redoArrayImage = [[NSMutableArray alloc]init ];
        _undoArrayImage = [[NSMutableArray alloc]init ];
    }
    return self;
}
-(void) setUpWithDefaultValue
{
    _maximumZoomScale = 2.0;
}
-(UIView*) viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return _scrollView.childView;
}
-(void) flipImage:(FlipDirection)direction
{
    UIImage *resultImage = _imageView.image;
    switch (direction) {
        case FlipLeft:
        {
            resultImage = [resultImage rotateInRadians:M_PI_2];
            break;
        }
        case FlipRight:
        {
            resultImage = [resultImage rotateInRadians:-M_PI_2];
            break;
        }  
        case FlipVertical:
        {
            resultImage = [resultImage verticalFlip];
            break;
        }  
        case FlipHorizontal:
        {
            resultImage = [resultImage horizontalFlip];
            break;
        }  
        default:
            break;
    }
    _imageView.image = resultImage;
    [self changeImageSizeAndUpdateScrollView];
}
-(void) startAction: (Action) action
{
    _currentAction = action;
    if (overlapView == nil) {
        
        overlapView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _imageView.frame.size.width, _imageView.frame.size.height)];
        overlapView.backgroundColor = [UIColor clearColor];
        overlapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        
        UIView *coverView;
        coverView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _imageView.frame.size.width, _imageView.frame.size.height)];
        coverView.backgroundColor = [UIColor clearColor];
        coverView.userInteractionEnabled = NO;
        coverView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        [overlapView addSubview:coverView];
    }
    else overlapView.frame = CGRectMake(0, 0, _imageView.frame.size.width, _imageView.frame.size.height);
    
    if (_cropAreaSelectionView!=nil) {
        _cropAreaSelectionView.frame = CGRectMake(0, 0, _imageView.frame.size.width, _imageView.frame.size.height);
    }
    else
    {
        _cropAreaSelectionView = [[ResizableView alloc]initWithFrame:CGRectMake(0, 0, _imageView.frame.size.width, _imageView.frame.size.height)] ;
        _cropAreaSelectionView.rotationDelegate = self;
    }
    _cropAreaSelectionView.userInteractionEnabled = YES;
    //[_cropAreaSelectionView changeResizeMethod:_resizeStrategy];
    [overlapView addSubview:_cropAreaSelectionView];
    
    _cropAreaSelectionView.action = _currentAction;

    // Set limit for move rectangle a round
    float zoomScale = 1.0;
    
 	CGRect rect;
	rect.origin.x = [_scrollView contentOffset].x * zoomScale;
	rect.origin.y = [_scrollView contentOffset].y * zoomScale;
	rect.size.width = [_scrollView bounds].size.width * zoomScale;
	rect.size.height = [_scrollView bounds].size.height * zoomScale;
    rect = CGRectIntersection(rect, _imageView.frame);
    
    _cropAreaSelectionView.displayArea = rect;
    if (_currentAction == ActionCrop) {
        
        
    [_cropAreaSelectionView changeResizeMethod:_resizeStrategy];
    }
    _lastScrollViewZoomScale = [_scrollView zoomScale];
    _lastImageViewFrame = _imageView.frame;
    [overlapView bringSubviewToFront:_cropAreaSelectionView];
    [_scrollView setScrollEnabled:NO];
    [_scrollView addSubview:overlapView];
    [_scrollView bringSubviewToFront:overlapView];
    
    _maximumZoomScale = _scrollView.maximumZoomScale;
    _scrollView.minimumZoomScale =1.0;
    _scrollView.maximumZoomScale = 1.0;
    _rotation =0;
}

-(void) finishCurrentAction
{
    CGRect selectionRect = _cropAreaSelectionView.selectionRect;
    float zoomScale;
    CGImageRef imageRef ;
    if (_currentAction == ActionStraighten) {
        zoomScale = 1.0/_lastScrollViewZoomScale;
        
    }
    else
        zoomScale =  1.0/ [_scrollView zoomScale];
    
    selectionRect.origin.x *= zoomScale;
    selectionRect.origin.y  *= zoomScale;
    selectionRect.size.width = selectionRect.size.width * zoomScale;
    selectionRect.size.height = selectionRect.size.height * zoomScale;
    UIImage *cropImage = _imageView.image;
    CGSize newSize;
    newSize.width = _imageView.frame.size.width*zoomScale;
    newSize.height = _imageView.frame.size.height *zoomScale;
    switch (_currentAction) {
        case ActionStraighten:
        {
            cropImage = [cropImage rotateInRadians:_rotation];
            selectionRect.origin.x = newSize.width/2-selectionRect.size.width/2;
            selectionRect.origin.y = newSize.height/2-selectionRect.size.height/2;
            [_cropAreaSelectionView resetAll];
            _imageView.transform = CGAffineTransformIdentity;
            break;
        }
        default:
        {
            break;
        }
    }
    
    cropImage = [cropImage resizedImage:newSize interpolationQuality:kCGInterpolationHigh];
    imageRef = CGImageCreateWithImageInRect([cropImage CGImage], selectionRect);
    UIImage *img = [UIImage imageWithCGImage:imageRef]; 
    CGImageRelease(imageRef);    
    _imageView.image  = img;
    [self changeImageSizeAndUpdateScrollView];
    [overlapView removeFromSuperview];
    [_scrollView setScrollEnabled:YES];
    [_cropAreaSelectionView removeFromSuperview];
    
}
-(void) changeImageSizeAndUpdateScrollView
{
    CGSize imageViewSize;
    CGFloat ratio;
    [self updateScrollView];
    UIImage *img = [[_imageView.image retain]autorelease];
    CGSize ratioSize = CGSizeMake(img.size.width, img.size.height);
    if ((img.size.width>0 && img.size.height>0 )) {
        
        if ( img.size.width>img.size.height) {
            ratio = ratioSize.height/ratioSize.width;
            imageViewSize.width = _scrollView.frame.size.width;
            imageViewSize.height = ratio * imageViewSize.width;
        }
        else
        {
            ratio = ratioSize.width/ratioSize.height;
            imageViewSize.height = _scrollView.frame.size.height;
            imageViewSize.width = ratio * imageViewSize.height;
        }
        _imageView.frame = CGRectMake(0,0, imageViewSize.width,imageViewSize.height);
        //NSLog(@"ImageViewSize: %@", NSStringFromCGSize(imageViewSize));
    }
    
    _scrollView.contentSize = _imageView.frame.size;
}
-(void) cancelCurrentAction
{
    [_cropAreaSelectionView removeFromSuperview];
    [overlapView removeFromSuperview];
    [_scrollView setScrollEnabled:YES];
    [self updateScrollView];
}
-(void) rotationChange:(CGFloat)rotation
{
    _rotation -=rotation;
    _imageView.transform =  CGAffineTransformRotate(_imageView.transform, rotation);
}
-(void) updateScrollView
{
    if(_imageView != nil) {
        CGSize imageSize = _imageView.image.size;
        CGSize scrollSize = _scrollView.frame.size;
        CGFloat widthRatio = scrollSize.width / imageSize.width;
        CGFloat heightRatio = scrollSize.height / imageSize.height;
        CGFloat minimumZoom = MIN(1.0, (widthRatio > heightRatio) ? heightRatio : widthRatio);
        _scrollView.minimumZoomScale = minimumZoom;
        
        if (_scrollView.maximumZoomScale != _maximumZoomScale) {
            [_scrollView setMaximumZoomScale:_maximumZoomScale];
        }
        [_scrollView setZoomScale:_scrollView.minimumZoomScale animated:YES];
        _lastScrollViewZoomScale = [_scrollView zoomScale];
	}	
	
}
//================================================================================================
-(BOOL) isCanRedo
{
    BOOL isCanRedo = NO;
    if (_redoArrayImage && [_redoArrayImage count]>0) {
        isCanRedo = YES;
    }
    return isCanRedo;
}
-(BOOL) isCanUndo
{
    BOOL isCanUndo = NO;
    if (_undoArrayImage && [_undoArrayImage count]>0) {
        isCanUndo = YES;
    }
    return isCanUndo;
}
-(void) redo
{
    if ([self isCanRedo]) {
        UIImage *redoImage = (UIImage*) [_redoArrayImage pop];
        if (redoImage) {
            _imageView.image = redoImage;
            if (_undoArrayImage) {
                [_undoArrayImage push:redoImage];
            }
        }
    }
}
-(void) undo
{
    if ([self isCanUndo]) {
        UIImage *undoImage = (UIImage*)[_undoArrayImage pop];
        if (undoImage) {
            _imageView.image = undoImage;
            if (_redoArrayImage) {
                [_redoArrayImage push:undoImage];
            }
        }
    }
}
-(void) saveImageToRedo:(UIImage*) repairingImage
{

    if (_redoArrayImage) {
        [_redoArrayImage push:repairingImage];
    }
    if (_undoArrayImage) {
        [_undoArrayImage removeAllObjects];
    }
}
-(void) cancelApplyCurrentEffect
{
    _isCanSlide = NO;
    _filter = NoneFilter;
        if ([self isCanRedo]) {
           _imageView.image = (UIImage*) [_redoArrayImage pop];
        }
}
-(void) doneCurrentEffect
{
    _isCanSlide = NO;
    _filter = NoneFilter;
}
//================================================================================================
-(void) applyEffect:(FilterAlgorithm) effect
{
    //slider.hidden = YES;
    _filter = effect;
    _originalImage = _imageView.image;
    [self saveImageToRedo:_originalImage];
    _isCanSlide = NO;
    switch (_filter) {
        case TintRed:
        {
            
            IFSimpleTintFilter *tinter = [[IFSimpleTintFilter alloc] initWithOriginalImage:_originalImage];
            tinter.tintColor = [UIColor redColor];
            _imageView.image = [tinter imageWithFilterApplied];
            [tinter release];
            
            break;
        }
        case Greyscale:
        {
            IFGreyscaleFilter *grey = [[IFGreyscaleFilter alloc] initWithOriginalImage:_originalImage];
            _imageView.image = [grey imageWithFilterApplied];
            [grey release];
            break;
        }
        case Pixelate :
        {
//            slider.minimumValue = 1.0;
//            slider.maximumValue = 20.0;
//            slider.value = 1.0;
//            sliderMode = IFFilterPreviewSliderModePixelate;
//            slider.hidden = NO;
//            [self sliderMoved:slider];
            _minimumSliderValue =1.0;
            _maximumSilderValue = 20.0;
            _currentSliderValue = 1.0;
            _isCanSlide = YES;
            [self sliderMoved:_currentSliderValue];
            break;
        }
        case Brightness:
        {
//            slider.minimumValue = -150.0;
//            slider.maximumValue = 150.0;
//            slider.value = 0.0;
//            sliderMode = IFFilterPreviewSliderModeBrightness;
//            slider.hidden = NO;
//            [self sliderMoved:slider];
            _minimumSliderValue =-150.0;
            _maximumSilderValue = 150.0;
            _currentSliderValue = 0.0;      
            _isCanSlide = YES;
            [self sliderMoved:_currentSliderValue];
            break;
        }
        case Saturation:
        {
//            slider.minimumValue = -150.0;
//            slider.maximumValue = 150.0;
//            slider.value = 0.0;
//            sliderMode = IFFilterPreviewSliderModeSaturation;
//            slider.hidden = NO;
//            [self sliderMoved:slider];
            _minimumSliderValue =-150.0;
            _maximumSilderValue = 150.0;
            _currentSliderValue = 0.0;
            _isCanSlide = YES;
            [self sliderMoved:_currentSliderValue];
            break;
        }
        case Hue :
        {
//            slider.minimumValue = 0;
//            slider.maximumValue = 360.0;
//            slider.value = 0.0;
//            sliderMode = IFFilterPreviewSliderModeHue;
//            slider.hidden = NO;
            _minimumSliderValue =0.0;
            _maximumSilderValue = 360.0;
            _currentSliderValue = 0.0;
            _isCanSlide = YES;
            [self sliderMoved:_currentSliderValue];
            break;
        }
        case Thermal:
        {
            IFThermalFilter *thermal = [[IFThermalFilter alloc] initWithOriginalImage:_originalImage];
            _imageView.image = [thermal imageWithFilterApplied];
            [thermal release];
            break;
        }
        case SnowFuzz:
        {
            IFSnowFuzzFilter *snow = [[IFSnowFuzzFilter alloc] initWithOriginalImage:_originalImage];
            _imageView.image = [snow imageWithFilterApplied];
            [snow release];
            break;
        }
        case Blur:
        {
            IFBlurFilter *blur = [[IFBlurFilter alloc] initWithOriginalImage:_originalImage];
            _imageView.image = [blur imageWithFilterApplied];
            [blur release];
            break;
        }
        case FindEdges:
        {
            IFFindEdgesFilter *edges = [[IFFindEdgesFilter alloc] initWithOriginalImage:_originalImage];
            _imageView.image = [edges imageWithFilterApplied];
            [edges release];
            break;
        }
        case Emboss:
        {
            IFEmbossFilter *emboss = [[IFEmbossFilter alloc] initWithOriginalImage:_originalImage];
            _imageView.image = [emboss imageWithFilterApplied];
            [emboss release];
            break;
        }
        case Sharpen:
        {
            IFSharpenFilter *sharpen = [[IFSharpenFilter alloc] initWithOriginalImage:_originalImage];
            _imageView.image = [sharpen imageWithFilterApplied];
            [sharpen release];
            break;
        }
        case TiltShift :
        {
            //activityView.hidden = NO;
            
            IFTiltShiftFilter *tilt = [[IFTiltShiftFilter alloc] initWithOriginalImage:_originalImage];
            tilt.delegate = self;
            //imageView.image = [tilt imageWithFilterApplied];
            [tilt applyFilterOnNewThread];
            [tilt release];
            break;
        }   
        default:
            break;
    }
}
- (void) sliderMoved:(float) newValue
{

    if(_filter == Pixelate)
	{
		IFPixelationFilter *pixels = [[IFPixelationFilter alloc] initWithOriginalImage:_originalImage];
		pixels.pixelSize = roundf(newValue);
		_imageView.image = [pixels imageWithFilterApplied];
		[pixels release];
	}
	else if(_filter ==Brightness) 
	{
		IFBrightnessFilter *brightness = [[IFBrightnessFilter alloc] initWithOriginalImage:_originalImage];
		brightness.brightnessAdjustment = roundf(newValue);
		_imageView.image = [brightness imageWithFilterApplied];
		[brightness release];
	}
	else if(_filter == Saturation) 
	{
		IFSaturationFilter *saturation = [[IFSaturationFilter alloc] initWithOriginalImage:_originalImage];
		saturation.saturationAdjustment = roundf(newValue);
		_imageView.image = [saturation imageWithFilterApplied];
		[saturation release];
	}
	
	else if(_filter == Hue) 
	{
		IFHueFilter *hue = [[IFHueFilter alloc] initWithOriginalImage:_originalImage];
		hue.hueAdjustment = roundf(newValue);
		_imageView.image = [hue imageWithFilterApplied];
		[hue release];
	}
  //  _currentSliderValue = newValue;
}
#pragma mark - Filter Delegate

- (void) filterDidApplyWithResult:(UIImage *)result
{
	//activityView.hidden = YES;
	_imageView.image = result;
}
//================================================================================================
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (_isCanSlide) {
    _lastTouchPosition = [[touches anyObject] locationInView:self];
    }
    
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (_isCanSlide) {
        CGPoint currentPosition = [[touches anyObject] locationInView:self];
        CGFloat distance = currentPosition.x - _lastTouchPosition.x;
        float unit = (_maximumSilderValue - _minimumSliderValue) /self.frame.size.width;
        distance = distance *unit;
        //NSLog(@"distance: %f", distance);
        float newValue=_currentSliderValue;
        newValue +=distance;
        
        if (newValue<_minimumSliderValue)
        {
            newValue = _minimumSliderValue;
        }
        if( newValue>_maximumSilderValue) {
            newValue = _maximumSilderValue;
        }
       // NSLog(@"newValue: %f", newValue);
        _currentSliderValue = newValue;
        _lastTouchPosition = currentPosition;
        [self sliderMoved:newValue];
    }
}
//================================================================================================

#pragma mark TapDetectingImageViewDelegate methods

- (void)handleSingleTap:(UIGestureRecognizer *)gestureRecognizer {
    // single tap does nothing for now
}

- (void)handleDoubleTap:(UIGestureRecognizer *)gestureRecognizer {
    // zoom in
    float newScale = [_scrollView zoomScale] * ZOOM_STEP;
    CGRect zoomRect = [self zoomRectForScale:newScale withCenter:[gestureRecognizer locationInView:gestureRecognizer.view]];
    [_scrollView zoomToRect:zoomRect animated:YES];
}

- (void)handleTwoFingerTap:(UIGestureRecognizer *)gestureRecognizer {

    // two-finger tap zooms out
    float newScale = [_scrollView zoomScale] / ZOOM_STEP;
    CGRect zoomRect = [self zoomRectForScale:newScale withCenter:[gestureRecognizer locationInView:gestureRecognizer.view]];
    [_scrollView zoomToRect:zoomRect animated:YES];
}

#pragma mark Utility methods

- (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center {
    
    CGRect zoomRect;
    
    // the zoom rect is in the content view's coordinates. 
    //    At a zoom scale of 1.0, it would be the size of the imageScrollView's bounds.
    //    As the zoom scale decreases, so more content is visible, the size of the rect grows.
    zoomRect.size.height = [_scrollView frame].size.height / scale;
    zoomRect.size.width  = [_scrollView frame].size.width  / scale;
    
    // choose an origin so as to get the right center.
    zoomRect.origin.x    = center.x - (zoomRect.size.width  / 2.0);
    zoomRect.origin.y    = center.y - (zoomRect.size.height / 2.0);
    
    return zoomRect;
}

- (void)dealloc
{
    [super dealloc];
}

@end
